

#include "ly/pcell_variant.hpp"
#include "ly/pcell_header.hpp"

#include <spdlog/spdlog.h>

namespace ly {

PCellVariant::PCellVariant(ly::cell_id_type cell_id, ly::Layout::sptr layout, ly::pcell_id_type pcell_id, const pcell_parameters_type& parameters)
    : Cell(cell_id, layout), m_parameters(parameters), m_pcell_id(pcell_id), m_registered(false) {
  PCellVariant::reregister();  // actually, no "re-register", but the first registration ..
}

// PCellVariant::~PCellVariant() {
//   PCellVariant::unregister();
// }

// Cell::sptr PCellVariant::clone(Layout& layout) const {
//   assert(m_registered);  // don't clone detached PCellVariant's
//   // Cell* cell = new PCellVariant(cell_index(), layout, m_pcell_id, m_parameters);
//   // //  copy the cell content
//   auto cell = PCellVariant::create(cell_id(), layout, m_pcell_id, m_parameters);
//   *cell = *this;
//   return cell;
// }

void PCellVariant::unregister() {
  // A PCellVariant that is saved as a member of a transaction will explicitly be unregistered ..
  if (m_registered) {
    auto header = pcell_header();
    if (header) {
      header->unregister_variant(this);
    }
    m_registered = false;
  }
}

void PCellVariant::reregister() {
  if (!m_registered) {
    auto header = pcell_header();
    if (header) {
      header->register_variant(this);
    }
    m_registered = true;
  }
}

// std::string PCellVariant::get_basic_name() {
//   auto header = pcell_header();
//   if (header) {
//     return header->get_name();
//   } else {
//     return get_basic_name();
//   }
// }

std::string PCellVariant::get_display_name() const {
  // const PCellHeader* header = pcell_header();
  // if (header) {
  //   if (m_display_name.empty()) {
  //     return header->get_name() + "*";
  //   } else {
  //     return m_display_name;
  //   }
  // } else {
  //   return Cell::get_basic_name();
  // }
  return m_display_name;
}

ly::Variant PCellVariant::parameter_by_name(const std::string& name) const {
  // const PCellHeader* header = pcell_header();
  // if (header && header->declaration()) {

  //   ly::pcell_parameters_type::const_iterator pp = parameters().begin();
  //   const std::vector<ly::PCellParameterDeclaration>& pcp = header->declaration()->parameter_declarations();
  //   for (std::vector<PCellParameterDeclaration>::const_iterator pd = pcp.begin(); pd != pcp.end() && pp != parameters().end(); ++pd, ++pp) {
  //     if (pd->get_name() == name) {
  //       return *pp;
  //     }
  //   }
  // }

  return ly::Variant();
}

std::map<std::string, ly::Variant> PCellVariant::parameters_by_name() const {
  return parameters_by_name_from_list(parameters());
}

std::map<std::string, ly::Variant> PCellVariant::parameters_by_name_from_list(const ly::pcell_parameters_type& list) const {
  std::map<std::string, ly::Variant> param_by_name;

  // const PCellHeader* header = pcell_header();
  // if (header && header->declaration()) {

  //   ly::pcell_parameters_type::const_iterator pp = list.begin();
  //   const std::vector<ly::PCellParameterDeclaration>& pcp = header->declaration()->parameter_declarations();
  //   for (std::vector<PCellParameterDeclaration>::const_iterator pd = pcp.begin(); pd != pcp.end() && pp != parameters().end(); ++pd, ++pp) {
  //     param_by_name.insert(std::make_pair(pd->get_name(), *pp));
  //   }
  // }

  return param_by_name;
}

// void PCellVariant::update(ImportLayerMapping* layer_mapping) {
//   tl_assert(layout() != 0);

//   clear_shapes();
//   clear_insts();

//   PCellHeader* header = pcell_header();
//   if (header && header->declaration()) {

//     ly::property_names_id_type pn = layout()->properties_repository().prop_name_id(ly::Variant("name"));
//     ly::property_names_id_type dn = layout()->properties_repository().prop_name_id(ly::Variant("description"));

//     std::vector<unsigned int> layer_ids;
//     try {

//       layer_ids = header->get_layer_indices(*layout(), m_parameters, layer_mapping);

//       //  call coerce prior to produce to make sure we have a validated parameter set
//       //  (note that we cannot persist parameters from here)
//       ly::pcell_parameters_type plist = m_parameters;
//       header->declaration()->coerce_parameters(*layout(), plist);

//       header->declaration()->produce(*layout(), layer_ids, plist, *this);

//       m_display_name = header->declaration()->get_display_name(plist);

//     } catch (ly::Exception& ex) {

//       ly::error << ex.msg();

//       //  put error messages into layout as text objects on error layer
//       shapes(layout()->error_layer()).insert(ly::Text(ex.msg(), ly::Trans()));
//     }

//     //  produce the shape parameters on the guiding shape layer so they can be edited
//     size_t i = 0;
//     const std::vector<ly::PCellParameterDeclaration>& pcp = header->declaration()->parameter_declarations();
//     for (std::vector<ly::PCellParameterDeclaration>::const_iterator p = pcp.begin(); p != pcp.end(); ++p, ++i) {

//       if (i < m_parameters.size() && p->get_type() == ly::PCellParameterDeclaration::t_shape && !p->is_hidden()) {

//         //  use property with name "name" to indicate the parameter name
//         ly::PropertiesRepository::properties_set props;
//         props.insert(std::make_pair(pn, ly::Variant(p->get_name())));

//         if (!p->get_description().empty()) {
//           props.insert(std::make_pair(dn, ly::Variant(p->get_description())));
//         }

//         if (m_parameters[i].is_user<ly::DBox>()) {

//           shapes(layout()->guiding_shape_layer())
//               .insert(ly::BoxWithProperties(ly::Box(m_parameters[i].to_user<ly::DBox>() * (1.0 / layout()->dbu())),
//                                             layout()->properties_repository().properties_id(props)));

//         } else if (m_parameters[i].is_user<ly::Box>()) {

//           shapes(layout()->guiding_shape_layer())
//               .insert(ly::BoxWithProperties(m_parameters[i].to_user<ly::Box>(), layout()->properties_repository().properties_id(props)));

//         } else if (m_parameters[i].is_user<ly::DEdge>()) {

//           shapes(layout()->guiding_shape_layer())
//               .insert(ly::EdgeWithProperties(ly::Edge(m_parameters[i].to_user<ly::DEdge>() * (1.0 / layout()->dbu())),
//                                              layout()->properties_repository().properties_id(props)));

//         } else if (m_parameters[i].is_user<ly::Edge>()) {

//           shapes(layout()->guiding_shape_layer())
//               .insert(ly::EdgeWithProperties(m_parameters[i].to_user<ly::Edge>(), layout()->properties_repository().properties_id(props)));

//         } else if (m_parameters[i].is_user<ly::DPoint>()) {

//           ly::DPoint p = m_parameters[i].to_user<ly::DPoint>();
//           shapes(layout()->guiding_shape_layer())
//               .insert(ly::PointWithProperties(ly::Point(p * (1.0 / layout()->dbu())), layout()->properties_repository().properties_id(props)));

//         } else if (m_parameters[i].is_user<ly::Point>()) {

//           ly::Point p = m_parameters[i].to_user<ly::Point>();
//           shapes(layout()->guiding_shape_layer()).insert(ly::PointWithProperties(p, layout()->properties_repository().properties_id(props)));

//         } else if (m_parameters[i].is_user<ly::DPolygon>()) {

//           ly::complex_trans<ly::DCoord, ly::Coord> dbu_trans(1.0 / layout()->dbu());
//           ly::Polygon poly = m_parameters[i].to_user<ly::DPolygon>().transformed(dbu_trans, false);
//           //  Hint: we don't compress the polygon since we don't want to loose information
//           shapes(layout()->guiding_shape_layer()).insert(ly::PolygonWithProperties(poly, layout()->properties_repository().properties_id(props)));

//         } else if (m_parameters[i].is_user<ly::Polygon>()) {

//           ly::Polygon poly = m_parameters[i].to_user<ly::Polygon>();
//           //  Hint: we don't compress the polygon since we don't want to loose information
//           shapes(layout()->guiding_shape_layer()).insert(ly::PolygonWithProperties(poly, layout()->properties_repository().properties_id(props)));

//         } else if (m_parameters[i].is_user<ly::DPath>()) {

//           ly::complex_trans<ly::DCoord, ly::Coord> dbu_trans(1.0 / layout()->dbu());
//           shapes(layout()->guiding_shape_layer())
//               .insert(
//                   ly::PathWithProperties(dbu_trans * m_parameters[i].to_user<ly::DPath>(), layout()->properties_repository().properties_id(props)));

//         } else if (m_parameters[i].is_user<ly::Path>()) {

//           shapes(layout()->guiding_shape_layer())
//               .insert(ly::PathWithProperties(m_parameters[i].to_user<ly::Path>(), layout()->properties_repository().properties_id(props)));
//         }
//       }
//     }
//   }
// }

}  // namespace ly
